import { assign, isNil, isNumber } from 'lodash-es'
import { BaseType, Data, serverTime } from '../index';
import { getDomain } from '../util'


const read = (val: string[] | string): string => {
  if (val[0] === '"') {
    val = val.slice(1, -1)
  }
  return (val as string).replace(/(%[\dA-F]{2})+/gi, decodeURIComponent)
}

const write = (val: string) => encodeURIComponent(val).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g, decodeURIComponent)

const get = (name?: string) => {
  const cookie = document.cookie;
  const cookies = cookie ? cookie.split('; ') : []
  const jar: Data = {}
  for (let i = 0; i < cookies.length; i++) {
    const parts = cookies[i].split('=')
    const value = parts.slice(1).join('=')
    try {
      const found = decodeURIComponent(parts[0])
      if (!(found in jar)) {
        jar[found] = read(value)
      }
      if (name === found) {
        break
      }
    } catch {
    }
  }
  return name ? jar[name] : jar
}

const set = (name: string, value: BaseType, options: Data = { path: '/' }): string => {
  if (isNumber(options.expires)) {
    options.expires = new Date(serverTime.value + options.expires * 864e5)
  }
  if (options.expires) {
    options.expires = options.expires.toUTCString()
  }

  name = encodeURIComponent(name)
    .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
    .replace(/[()]/g, escape)

  let stringifyOptions = ''
  for (const option in options) {
    if (options[option]) {
      stringifyOptions += '; ' + option
      if (options[option] !== true) {
        stringifyOptions += '=' + options[option].split(';')[0]
      }
    }
  }
  return (document.cookie = name + '=' + write(value as string) + stringifyOptions)
}

const remove = (name: string, options: any = { path: '/' }) => set(name, '', assign({}, options, { expires: -1 }))

export function getCookie(name: string): string | null {
  const tmp = get(name)
  return tmp === undefined ? null : tmp
}

export function setCookie(name: string, value: any, expires?: number, domain?: string): void {
  if (name && !isNil(value)) {
    domain || (domain = getDomain())
    removeCookie(name, domain)
    if (isNil(expires)) {
      set(name, value, { domain, path: '/', SameSite: 'Strict' })
    } else {
      set(name, value, { domain, path: '/', expires, SameSite: 'Strict' })
    }
  }
}

export function removeCookie(name: string, domain?: string): void {
  remove(name)
  domain || (domain = getDomain())
  remove(name, { domain })
}

export function clearAllCookies(domain?: string): void {
  Object.keys(get()).forEach(name => removeCookie(name, domain))
}

export function allCookies() {
  const cookie = document.cookie
  const cookies = cookie ? cookie.split('; ') : []
  const outs: any = []
  for (let i = 0; i < cookies.length; i++) {
    const parts = cookies[i].split('=')
    const key = decodeURIComponent(parts[0])
    const value = parts.slice(1).join('=')
    outs.push({ key, value })
  }
  return outs
}
